import 'dart:collection';
import 'dart:io';
import 'package:xsdk_flutter/xsdk/operation/x_constant_temp.dart';
import 'package:xsdk_flutter/xsdk/operation/x_mixin_release.dart';
import 'package:xsdk_flutter/xsdk/utils/queue/x_bean_queue_message.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_date.dart';
import 'package:xsdk_flutter/xsdk/utils/json/x_utils_json.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_share_preferences.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_string.dart';

import 'package:async/async.dart';

typedef XFunctionNotifyHandleQueueMsg<T extends XBaseQueueMessage> = void Function(T msg);
typedef XFunctionNotifyTempMsgQueueIdleAndMsgQueueHasItem = void Function();
typedef XFunctionNotifyHandleHeartbeat = void Function();



/*
队列处理器
支持 并发模式 或 阻塞逐一消费模式(等待上一条确认完成再消费下一条)
注意:
msgId不为空时,才会留底保存到本地(支持重打机制),否则成功或失败只是提示,不重打,不本地化保存;

由于flutter是单线程,在异步中的队列循环中,每次完毕需要使用sleep释放cpu占用,避免高峰期长时间占用cpu造成其他队列任务延迟
 */
class XQueue with XMixinRelease {

  //队列名称
  late String queueName;
  //通知取出当前临时队列消息待处理
  late XFunctionNotifyHandleQueueMsg notifyHandleMsg;
  //通知存在临时消息列队空闲(留底队列不为空)
  late XFunctionNotifyTempMsgQueueIdleAndMsgQueueHasItem notifyTempMsgQueueIdleAndMsgQueueHasItem;
  //通知处理心跳包
  late XFunctionNotifyHandleHeartbeat notifyHandleHeartbeat;


  //是否并发(并发则不用等待上一条消息成功通知继续下一条消息消费)
  bool isConcurrentMode=false;
  //是否需要本地化保存留底,用于恢复
  bool isNeedSaveSrcMsgBySP;

  //是否开启心跳包
  bool isEnableHeartBeat=false;
  //心跳包时间10秒
  int heartbeatTime;
  //心跳包的Future(可取消)
  CancelableOperation? _heartBeatCancelableOperation;

  XQueue(
      {
        required this.queueName,
        required this.notifyHandleMsg,
        required this.notifyTempMsgQueueIdleAndMsgQueueHasItem,
        required this.notifyHandleHeartbeat,
        this.isConcurrentMode=false, //并发
        this.isEnableHeartBeat=false, //心跳包
        this.isNeedSaveSrcMsgBySP=false,
        this.heartbeatTime=10, //心跳时间,秒
      }
      );

  @override
  void xRelease() {
    //释放资源
    _heartBeatCancelableOperation?.cancel();
    _isLoop =false;
    _msgQueue.clear();
    _tempMsgQueue.clear();
    _isInit=false;

  }


  //1.初始化(支持切换多用户)ok
  bool _isInit=false;//防止重复初始化造成重复子线程
  void init(){

    //1.1.初始化消息队列
    _initMessageList();

    //防止多次初始化重复,造成重复子线程(调用自身实现循环)
    if(_isInit){
      return;
    }
    _isInit=true;

    //1.2.开启心跳包
    if(isEnableHeartBeat){
      _heartBeatCancelableOperation=CancelableOperation.fromFuture(
          Future.delayed(Duration(seconds:heartbeatTime),(){
            if(_heartBeatCancelableOperation?.isCanceled==false){
              _loopHandleHeartBeat();
            }

          })
      );
    }

    //1.3避免重复开启多个循环
    if(_tempMsgQueue.isNotEmpty){
      _isHasStartTempMsgListThreadLoop=true;
      //开启打印消息队列监听(这里延迟10秒启动,适应在前面如硬件做一些初始化工作的情况,BLE设备会慢一点)
      Future.delayed(const Duration(seconds:10),(){
        _startTempMsgListThreadLoop();
      });
    }

  }

  //2.添加新的待消费消息到队列(交给队列循环遍历自动处理) ok
  void addNewMsgToQueueForWaitQueueAutoHandle(XBaseQueueMessage item){

    //同一个消息分开留底和临时队列中保存,有利于临时队列的遍历响应效率

    //留底消息队列
    if (item.isSaveToQueueSrcMsgList){
      if (item.isPriorityHandle){
        //优先打印
        _msgQueue.addFirst(item);
      }else{
        _msgQueue.add(item);
      }
      //保存更新
      _saveAndUpdateMsgQueue();
    }

    //临时消息队列
    if (item.isPriorityHandle){
      //优先打印
      //_tempMsgQueue.add(0,item);
      _tempMsgQueue.addFirst(item);
    }else{
      _tempMsgQueue.add(item);
    }
    //XLogUtils.printLog('---_tempMsgQueue=${_tempMsgQueue.length}');

    //通知临时消息队列有新消息,开始轮询
    _notifyTempMsgQueueHasItem();

  }


  //3.外界调用处理成功完成的消息,解锁和清除留底消息,还可获得留底信息对比(如用于快麦api请求后对应原来的消息) (外界在成功消费通知回调中调用) ok
  XBaseQueueMessage? handleSucceedMessageAndUnLockTempMessageQueue(String? msgId){

    XBaseQueueMessage? msg;

    if (!XStringUtils.isEmpty(msgId)){
      _msgQueue.removeWhere((XBaseQueueMessage item){
        if (msgId==item.msgId) {
          msg=item;
          return true;
        }else{
          return false;
        }
      });

      //保存(注意:要在迭代器外面保存,迭代器没结束,获取队列的数量还是旧的,不准确)
      _saveAndUpdateMsgQueue();
    }

    //解除临时消息队列锁
    _tempMessageQueueLocking =false;


    if (!isConcurrentMode){
      //由于并发模式已在其中调用自身开启下一次循环了,这里针对非并发模式,在每次解锁后,

      //再次判断临时消息队列是否为空,开启下一次循环
      if(_tempMsgQueue.isNotEmpty){
        //但iOS平台真的在单线程执行,不能直接调_startTempMsgListThreadLoop();要套用新的Future();否则当前Future无法结束,导致其他Future阻塞
        Future((){
          _startTempMsgListThreadLoop();
        });
      }else{
        //结束当前循环
        _isHasStartTempMsgListThreadLoop=false;
      }

    }

    return msg;

  }

  //4.外面调用处理失败消息,解锁和重试 (外界在失败消费通知回调中调用) ok
  XBaseQueueMessage? handleFailedMessageAndUnlockTempMessageQueue(String? msgId,String errorLog){

    XBaseQueueMessage? msg;

    //注意:只有msgId不为空才留底,重新设置打印失败消息打印
    if (!XStringUtils.isEmpty(msgId)){

      for(XBaseQueueMessage item in _msgQueue){
        //自动(按限制次数)重打,再次加入临时队列
        if (msgId==item.msgId){
          msg=item;
          //自动重试
          if(item.isAutoRetry){
            if(item.limitTryCount >0){
              item.limitTryCount--;
              item.errorLog=errorLog;
              if(isConcurrentMode){
                //并发
                _tempMsgQueue.add(item);
              }else{
                //按顺序
                _tempMsgQueue.addFirst(item);
              }

              if (item.limitTryCount==0){
                //保存(注意:在迭代器内保存,迭代器没结束,获取队列的数量还是旧的,不准确,但这里不是更改数量因此无所谓)
                _saveAndUpdateMsgQueue();
              }
            }
          }

          break;
        }
      }

      //删除不自动重试失败的留底信息,避免堆积
      if(msg!=null && !msg.isAutoRetry){
        _msgQueue.removeWhere((XBaseQueueMessage item){
          if (msgId==item.msgId) {
            return true;
          }else{
            return false;
          }
        });

        //保存(注意:要在迭代器外面保存,迭代器没结束,获取队列的数量还是旧的,不准确)
        _saveAndUpdateMsgQueue();
      }

    }

    //解除临时消息队列锁
    _tempMessageQueueLocking =false;


    if (!isConcurrentMode){
      //由于并发模式已在其中调用自身开启下一次循环了,这里针对非并发模式,在每次解锁后,

      //再次判断临时消息队列是否为空,开启下一次循环
      if(_tempMsgQueue.isNotEmpty){
        //但iOS平台真的在单线程执行,不能直接调_startTempMsgListThreadLoop();要套用新的Future();否则当前Future无法结束,导致其他Future阻塞
        Future((){
          _startTempMsgListThreadLoop();
        });
      }else{
        //结束当前循环
        _isHasStartTempMsgListThreadLoop=false;
      }

    }

    return msg;
  }


  //未使用,用于空闲时是否判断恢复5次失败打印...////////////////////////////////////////

  //外界调用恢复所有失败消息重新打印
  void regainAllFailedMessages_limitTryCount_0(){
    for(XBaseQueueMessage item in _msgQueue){
      if (item.limitTryCount<=0 && item.isAutoRetry){
        item.limitTryCount=item.defaultTryCount;
        _tempMsgQueue.add(item);
      }
    }
  }


  //外界调用删除指定的留底信息(适用如空闲时外界遍历留底列表,符合条件则删除,注意: 这里不持久化删除) ok
  void removeItemFromMsgQueue(XBaseQueueMessage t){
    _msgQueue.remove(t);
  }

  //private///////////////////////////////////////////////////////////////////

  //更新保存消息队列 ok
  void _saveAndUpdateMsgQueue(){
    if(isNeedSaveSrcMsgBySP){
      String userGroup=XSharePreferencesUtils.getString(XConstants.SP_UserGroup);//多用户保存
      XSharePreferencesUtils.saveString('$userGroup${XConstants.SP_XQueue_Msg}$queueName',XJsonUtils.obj2Json(_msgQueue));
    }
  }

  //开启心跳包(调用自身循环) ok
  void _loopHandleHeartBeat(){

    try{
      //退出
      if(!_isLoop){
        return;
      }

      //通知处理心跳包
      Future((){
        notifyHandleHeartbeat();
      });

      //检查通知外界进一步处理(也可不处理)
      if(_tempMsgQueue.isEmpty && _msgQueue.isNotEmpty){
        //XLogUtils.printLog('------$queueName,通知存在临时消息列队空闲(留底队列不为空)');
        notifyTempMsgQueueIdleAndMsgQueueHasItem();
      }

    }catch(e){
      //
    }finally{

      //退出
      if(!_isLoop){
        return;
      }

      //循环调用自己,延迟10秒
      _heartBeatCancelableOperation=CancelableOperation.fromFuture(
          Future.delayed(Duration(seconds:heartbeatTime),(){
            if(_heartBeatCancelableOperation?.isCanceled==false){
              _loopHandleHeartBeat();
            }
          })
      );

    }

  }


  //是否正在调用自身循环中
  bool _isHasStartTempMsgListThreadLoop=false;

  //通知临时消息队列有新消息,开始轮询 ok
  void _notifyTempMsgQueueHasItem(){

    //已停止调用自身循环,重新开启
    if(!_isHasStartTempMsgListThreadLoop){
      _isHasStartTempMsgListThreadLoop=true;
      //XLogUtils.printLog('---_notifyTempMsgQueueHasItem,通知开启队列循环');
      Future((){
        _startTempMsgListThreadLoop();
      });
    }

  }

  //是否正在循环队列(总开关)
  bool _isLoop =true;

  //消息队列(留底)
  final Queue<XBaseQueueMessage> _msgQueue= ListQueue();
  //消息队列(临时,不用堆积失败消息,更快处理最新消息)
  final Queue<XBaseQueueMessage> _tempMsgQueue= ListQueue();

  //是否锁定临时消息队列(按顺序执行完成再执行下一条)
  bool _tempMessageQueueLocking=false;

  //启动临时消息队列监听
  // 注意:要开启线程调用,否则如果内部有sleep()会阻塞其他Future,因为当前Future在iOS平台(单线程)无法结束
  // 注意:不要重复开启,因为调用自身实现循环,否则造成重复) ok
  void _startTempMsgListThreadLoop() async{

    _isHasStartTempMsgListThreadLoop=true;
    //XLogUtils.printLog('---_startTempMsgListThreadLoop开启...');

    try {

      //退出
      if(!_isLoop){
        return;
      }

      if (isConcurrentMode){
        //并发处理(无需等待)

        if (_tempMsgQueue.isNotEmpty){

          XBaseQueueMessage msgItem= _tempMsgQueue.removeFirst();

          if (!msgItem.isHeartbeatCommand){
            XLogUtils.printLog('---$queueName,临时消息队列监听,取出msgId= ${msgItem.msgId},剩余:_tempMsgQueue=${_tempMsgQueue.length}');
          }else{
            XLogUtils.printLog('---$queueName,临时消息队列监听,取出心跳',tag: 'XOther');
          }

          //修改时间
          msgItem.modifyTime = XDateUtils.getStringTimeFormat(format: XDateUtils.formatTimeDefault2);
          //通知取出当前临时队列消息待处理

          //循环优化适配高峰期,需要sleep一下,释放cpu处理其他任务,
          sleep(const Duration(milliseconds: 100));

          Future((){
            notifyHandleMsg(msgItem);
          });

          //再次判断临时消息队列是否为空
          if(_tempMsgQueue.isNotEmpty){

            //循环优化适配高峰期,需要sleep一下,释放cpu处理其他任务,
            sleep(const Duration(milliseconds: 100));

            //但iOS平台真的在单线程执行,不能直接调_startTempMsgListThreadLoop();,要套用新的Future();否则当前Future无法结束,导致其他Future阻塞
            Future((){
              _startTempMsgListThreadLoop();
            });

          }else{
            //结束当前循环
            _isHasStartTempMsgListThreadLoop=false;
          }

        }else{
          //结束当前循环
          _isHasStartTempMsgListThreadLoop=false;
        }

      }else{
        //逐一处理(需等待上一消息确认完成再处理下一条消息)

        if (!_tempMessageQueueLocking){

          //队列不在锁定中,可以取出下一条消息
          if (_tempMsgQueue.isNotEmpty){

            //自动上锁,暂停队列消费下一条消息
            _tempMessageQueueLocking =true;

            XBaseQueueMessage msgItem= _tempMsgQueue.removeFirst();

            if (!msgItem.isHeartbeatCommand){
              XLogUtils.printLog('---$queueName,临时消息队列监听,取出msgId= ${msgItem.msgId},剩余:_tempMsgQueue=${_tempMsgQueue.length}');
            }else{
              XLogUtils.printLog('---$queueName,临时消息队列监听,取出心跳',tag: 'XOther');
            }

            //修改时间
            msgItem.modifyTime = XDateUtils.getStringTimeFormat(format: XDateUtils.formatTimeDefault2);
            //通知取出当前临时队列消息待处理
            Future((){
              notifyHandleMsg(msgItem);
            });

            //注意:不在这里启动下一次循环,改为在解锁方法里后检查启动下一次循环,为了适配iOS平台单线程,由于Future优先级高于dio库,避免占用CPU资源导致dio无法快速响应执行

          }else{
            //结束当前循环
            _isHasStartTempMsgListThreadLoop=false;
          }

        }else{
          XLogUtils.printLog('---$queueName,正处在锁定队列中,等待...');
        }

      }


    }catch (error, stacktrace) {
      XLogUtils.printLog('---$queueName,error=$error, stacktrace=$stacktrace');
      //结束当前循环
      _isHasStartTempMsgListThreadLoop=false;
    }

  }

  //初始化消息队列 ok
  void _initMessageList(){

    //1.恢复持久化队列任务 到 留底自动重试 消息队列
    _msgQueue.clear();
    String userGroup=XSharePreferencesUtils.getString(XConstants.SP_UserGroup);//多用户保存
    String json0= XSharePreferencesUtils.getString('$userGroup${XConstants.SP_XQueue_Msg}$queueName');
    if (!XStringUtils.isEmpty(json0)){
      List<dynamic> list= XJsonUtils.json2List(json0);
      for(dynamic item in list){
        XBaseQueueMessage itemMsg=XBaseQueueMessage.fromJson(item);
        if(itemMsg.isAutoRetry){
          _msgQueue.add(itemMsg);
        }
      }
    }

    //更新
    _saveAndUpdateMsgQueue();

    //2.恢复留底自动重试消息
    _regainAllFailedMessages();

  }

  //重新恢复所有失败消息 ok
  void _regainAllFailedMessages(){
    _tempMsgQueue.clear();
    for(XBaseQueueMessage item in _msgQueue){
      if(item.isAutoRetry){
        item.limitTryCount=item.defaultTryCount;
        _tempMsgQueue.add(item);
      }
    }
  }

}